package com.ruoyi.web.controller.test;

import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.uuid.UUID;
import io.swagger.models.auth.In;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Author: lb
 * @Date: 2021/12/10 14:46
 * @描述：测试 <p>
 * RedisTestController
 */
@RestController()
@RequestMapping("/test")
public class RedisTestController {
    private static final Logger log = LoggerFactory.getLogger(RedisTestController.class);
    @Autowired
    private RedisCache redisCache;

    /**
     * redis模糊分页查询list
     *
     * @param pattern
     * @return
     */
    @GetMapping("/redisTest")
    public Map test(@RequestParam String pattern) {
        String key = "*" + pattern + "*";
        long total = redisCache.keys(key).size();
        Set<String> keys = redisCache.findKeysForPage(key, 1, 5);
        List list = redisCache.getMultiList(keys);
        Map map = new HashMap<>();
        map.put("total", total);
        map.put("data", list);
        return map;
    }

    /**
     * redis分布式加锁
     * @return
     */
    @GetMapping("/redisLockTest")
    public String redisLockTest(@RequestParam("num") Integer num){
        String lockKey = "REDIS_LOCK_KEY_" + num;
        String lockValue = UUID.randomUUID().toString();
        int expireTime = 30;

        for (int i = 0; i < 3; i++) {
            int currIndex = i + 1;
            new Thread(){
                @Override
                public void run() {
                    try {
                        String beginDate = DateUtils.getTime();
                        boolean lock = tryLock(redisCache, lockKey, lockValue, expireTime);
                        log.info("第{}个加锁状态-{}", currIndex, lock);

                        int sum = FactorialCalculation(num + currIndex);
                        log.info("第{}个输出结果：{}", currIndex, sum);

                        String endDate = DateUtils.getTime();
                        log.info("执行完毕 第{}个线程进入时间：{} --结束时间：{}", currIndex, beginDate, endDate);

                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        boolean unLock = unLock(redisCache, lockKey, lockValue);
                        log.info("第{}个解锁状态-{}", currIndex, unLock);
                    }
                }
            }.start();
        }

        return "加锁测试完成...";
    }

    /**
     * 加锁
     * @param redisCache Redis客户端
     * @param lockKey 锁key
     * @param lockValue 锁value
     * @param expireTime 过期时间
     * @return 是否成功
     */
    public static boolean tryLock(RedisCache redisCache, String lockKey, String lockValue, int expireTime) {
        long outTime = System.currentTimeMillis() + expireTime * 1000;

        while(true){
            //判断是否超过了，尝试获取锁的时间
            if(System.currentTimeMillis() > outTime){
                return false;
            }
            //尝试获取锁
            boolean result = redisCache.setIfAbsentObject(lockKey,lockValue,expireTime, TimeUnit.SECONDS);
            if (result) {
                return true;//获取锁成功
            }

            try {
                //获得锁失败，休眠100毫秒再去尝试获得锁,避免一直请求redis，导致redis cpu飙升
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**

     * 解锁
     * @param redisCache Redis客户端
     * @param lockKey 锁key
     * @param lockValue 锁value
     * @return 是否释放成功
     */
    public static boolean unLock(RedisCache redisCache, String lockKey, String lockValue) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = redisCache.redisEval(script, lockKey, lockValue);
        if ("1".equals(result.toString())) {
            return true;
        }
        return false;
    }

    /**
     * 计算阶乘
     * @param num
     * @return
     */
    public static int FactorialCalculation(Integer num){
        int calSum = 1;
        for (int i = 1; i < num; i++) {
            calSum = calSum * i;
        }
        return calSum;
    }

}
